{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "<img src=\"../../img/ods_stickers.jpg\">\n",
    "\n",
    "## <center> [mlcourse.ai](https://mlcourse.ai) – открытый курс OpenDataScience по машинному обучению \n",
    "    \n",
    "Авторы материала: Ольга Дайховская (@aiho в Slack ODS), Юрий Кашницкий (@yorko в Slack ODS). Материал распространяется на условиях лицензии [Creative Commons CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/). Можно использовать в любых целях (редактировать, поправлять и брать за основу), кроме коммерческих, но с обязательным упоминанием автора материала."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# <center>Домашнее задание № 7 (демо)\n",
    "## <center> Обучение без учителя: метод главных компонент и кластеризация"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В этом задании мы разберемся с тем, как работают методы снижения размерности и кластеризации данных. Заодно еще раз попрактикуемся в задаче классификации.\n",
    "\n",
    "Мы будем работать с набором данных [Samsung Human Activity Recognition](https://archive.ics.uci.edu/ml/datasets/Human+Activity+Recognition+Using+Smartphones). Скачайте данные [отсюда](https://drive.google.com/file/d/14RukQ0ylM2GCdViUHBBjZ2imCaYcjlux/view?usp=sharing). Данные поступают с акселерометров и гироскопов мобильных телефонов Samsung Galaxy S3 (подробнее про признаки – по ссылке на UCI выше), также известен вид активности человека с телефоном в кармане – ходил ли он, стоял, лежал, сидел или шел вверх/вниз по лестнице. \n",
    "\n",
    "Вначале мы представим, что вид активности нам неизвестнен, и попробуем кластеризовать людей чисто на основе имеющихся признаков. Затем решим задачу определения вида физической активности именно как задачу классификации. \n",
    "\n",
    "Заполните код в клетках (где написано \"Ваш код здесь\") и ответьте на вопросы в [веб-форме](https://docs.google.com/forms/d/1qzcrfsNFy-e4TW59v2fqMj_OTom2SIOxtq4MWlI92p0)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import seaborn as sns\n",
    "from tqdm import tqdm_notebook\n",
    "\n",
    "%matplotlib inline\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "plt.style.use(['seaborn-darkgrid'])\n",
    "plt.rcParams['figure.figsize'] = (12, 9)\n",
    "plt.rcParams['font.family'] = 'DejaVu Sans'\n",
    "\n",
    "from sklearn import metrics\n",
    "from sklearn.cluster import AgglomerativeClustering, KMeans, SpectralClustering\n",
    "from sklearn.decomposition import PCA\n",
    "from sklearn.model_selection import GridSearchCV\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "from sklearn.svm import LinearSVC\n",
    "\n",
    "RANDOM_STATE = 17"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train = np.loadtxt(\"../../data/samsung_HAR/samsung_train.txt\")\n",
    "y_train = np.loadtxt(\"../../data/samsung_HAR/samsung_train_labels.txt\").astype(int)\n",
    "\n",
    "X_test = np.loadtxt(\"../../data/samsung_HAR/samsung_test.txt\")\n",
    "y_test = np.loadtxt(\"../../data/samsung_HAR/samsung_test_labels.txt\").astype(int)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Проверим размерности\n",
    "assert(X_train.shape == (7352, 561) and y_train.shape == (7352,))\n",
    "assert(X_test.shape == (2947, 561) and y_test.shape == (2947,))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для кластеризации нам не нужен вектор ответов, поэтому будем работать с объединением обучающей и тестовой выборок. Объедините *X_train* с *X_test*, а *y_train* – с *y_test*. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь\n",
    "X = \n",
    "y = "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Определим число уникальных значений меток целевого класса."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.unique(y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n_classes = np.unique(y).size"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Эти метки соответствуют:](https://archive.ics.uci.edu/ml/machine-learning-databases/00240/UCI%20HAR%20Dataset.names)\n",
    "- 1 - ходьбе\n",
    "- 2 - подъему вверх по лестнице\n",
    "- 3 - спуску по лестнице\n",
    "- 4 - сидению\n",
    "- 5 - стоянию\n",
    "- 6 - лежанию\n",
    "\n",
    "*уж простите, если звучание этих существительных кажется корявым :)*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Отмасштабируйте выборку с помощью `StandardScaler` с параметрами по умолчанию."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь\n",
    "scaler = \n",
    "X_scaled = "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Понижаем размерность с помощью PCA, оставляя столько компонент, сколько нужно для того, чтобы объяснить как минимум 90% дисперсии исходных (отмасштабированных) данных. Используйте отмасштабированную выборку и зафиксируйте random_state (константа RANDOM_STATE)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь\n",
    "pca = \n",
    "X_pca = "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Вопрос 1:**<br>\n",
    "Какое минимальное число главных компонент нужно выделить, чтобы объяснить 90% дисперсии исходных (отмасштабированных) данных?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Варианты:**\n",
    "- 56 \n",
    "- 65\n",
    "- 66\n",
    "- 193"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Вопрос 2:**<br>\n",
    "Сколько процентов дисперсии приходится на первую главную компоненту? Округлите до целых процентов. \n",
    "\n",
    "**Варианты:**\n",
    "- 45\n",
    "- 51\n",
    "- 56\n",
    "- 61"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Визуализируйте данные в проекции на первые две главные компоненты."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь\n",
    "plt.scatter(, , c=y, s=20, cmap='viridis');"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Вопрос 3:**<br>\n",
    "Если все получилось правильно, Вы увидите сколько-то кластеров, почти идеально отделенных друг от друга. Какие виды активности входят в эти кластеры?<br>\n",
    "\n",
    "**Ответ:**\n",
    "- 1 кластер: все 6 активностей\n",
    "- 2 кластера: (ходьба, подъем вверх по лестнице, спуск по лестнице) и (сидение, стояние, лежание)\n",
    "- 3 кластера: (ходьба), (подъем вверх по лестнице, спуск по лестнице) и (сидение, стояние, лежание)\n",
    "- 6 кластеров"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "------------------------------"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Сделайте кластеризацию данных методом `KMeans`, обучив модель на данных со сниженной за счет PCA размерностью. В данном случае мы подскажем, что нужно искать именно 6 кластеров, но в общем случае мы не будем знать, сколько кластеров надо искать.\n",
    "\n",
    "Параметры:\n",
    "\n",
    "- **n_clusters** = n_classes (число уникальных меток целевого класса)\n",
    "- **n_init** = 100\n",
    "- **random_state** = RANDOM_STATE (для воспроизводимости результата)\n",
    "\n",
    "Остальные параметры со значениями по умолчанию."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Визуализируйте данные в проекции на первые две главные компоненты. Раскрасьте точки в соответствии с полученными метками кластеров."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь\n",
    "plt.scatter(, , c=cluster_labels, s=20,  cmap='viridis');"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Посмотрите на соответствие между метками кластеров и исходными метками классов и на то, какие виды активностей алгоритм `KMeans` путает."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tab = pd.crosstab(y, cluster_labels, margins=True)\n",
    "tab.index = ['ходьба', 'подъем вверх по лестнице', \n",
    "             'спуск по лестнице', 'сидение', 'стояние', 'лежание', 'все']\n",
    "tab.columns = ['cluster' + str(i + 1) for i in range(6)] + ['все']\n",
    "tab"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Видим, что каждому классу (т.е. каждой активности) соответствуют несколько кластеров. Давайте посмотрим на максимальную долю объектов в классе, отнесенных к какому-то одному кластеру. Это будет простой метрикой, характеризующей, насколько легко класс отделяется от других при кластеризации. \n",
    "\n",
    "Пример: если для класса \"спуск по лестнице\", в котором 1406 объектов,  распределение кластеров такое:\n",
    " - кластер 1 – 900\n",
    " - кластер 3 – 500\n",
    " - кластер 6 – 6,\n",
    " \n",
    "то такая доля будет 900 / 1406 $\\approx$ 0.64.\n",
    " \n",
    "\n",
    "**Вопрос 4:**<br>\n",
    "Какой вид активности отделился от остальных лучше всего в терминах простой  метрики, описанной выше?<br>\n",
    "\n",
    "**Ответ:**\n",
    "- ходьба\n",
    "- стояние\n",
    "- спуск по лестнице\n",
    "- перечисленные варианты не подходят"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Видно, что kMeans не очень хорошо отличает только активности друг от друга. Используйте метод локтя, чтобы выбрать оптимальное количество кластеров. Параметры алгоритма и данные используем те же, что раньше, меняем только `n_clusters`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь\n",
    "inertia = []\n",
    "for k in tqdm_notebook(range(1, n_classes + 1)):\n",
    "    #\n",
    "    #"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Вопрос 5:**<br>\n",
    "Какое количество кластеров оптимально выбрать, согласно методу локтя?<br>\n",
    "\n",
    "**Ответ:**\n",
    "- 1\n",
    "- 2\n",
    "- 3\n",
    "- 4"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "------------------------"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Попробуем еще один метод кластеризации, который описывался в статье – агломеративную кластеризацию."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ag = AgglomerativeClustering(n_clusters=n_classes, \n",
    "                             linkage='ward').fit(X_pca)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Посчитайте Adjusted Rand Index (`sklearn.metrics`) для получившегося разбиения на кластеры и для `KMeans` с параметрами из задания к 4 вопросу."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Вопрос 6:**<br>\n",
    "Отметьте все верные утверждения.<br>\n",
    "\n",
    "**Варианты:**\n",
    "- Согласно ARI, KMeans справился с кластеризацией хуже, чем Agglomerative Clustering\n",
    "- Для ARI не имеет значения какие именно метки присвоены кластерам, имеет значение только разбиение объектов на кластеры\n",
    "- В случае случайного разбиения на кластеры ARI будет близок к нулю"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "-------------------------------"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Можно заметить, что задача не очень хорошо решается именно как задача кластеризации, если выделять несколько кластеров (> 2). Давайте теперь решим задачу классификации, вспомнив, что данные у нас размечены.  \n",
    "\n",
    "Для классификации используйте метод опорных векторов – класс `sklearn.svm.LinearSVC`. Мы в курсе отдельно не рассматривали этот алгоритм, но он очень известен, почитать про него можно, например, в материалах Евгения Соколова –  [тут](https://github.com/esokolov/ml-course-msu/blob/master/ML16/lecture-notes/Sem11_linear.pdf). \n",
    "\n",
    "Настройте для `LinearSVC` гиперпараметр `C` с помощью `GridSearchCV`. \n",
    "\n",
    "- Обучите новый `StandardScaler` на обучающей выборке (со всеми исходными признаками), прмиените масштабирование к тестовой выборке\n",
    "- В `GridSearchCV` укажите  cv=3."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь\n",
    "#\n",
    "X_train_scaled = \n",
    "X_test_scaled = "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "svc = LinearSVC(random_state=RANDOM_STATE)\n",
    "svc_params = {'C': [0.001, 0.01, 0.1, 1, 10]}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь\n",
    "best_svc = "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ваш код здесь"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Вопрос 7**<br>\n",
    "Какое значение гиперпараметра `C` было выбрано лучшим по итогам кросс-валидации?<br>\n",
    "\n",
    "**Ответ:**\n",
    "- 0.001\n",
    "- 0.01\n",
    "- 0.1\n",
    "- 1\n",
    "- 10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_predicted = best_svc.predict(X_test_scaled)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tab = pd.crosstab(y_test, y_predicted, margins=True)\n",
    "tab.index = ['ходьба', 'подъем вверх по лестнице', 'спуск по лестнице', \n",
    "             'сидение', 'стояние', 'лежание', 'все']\n",
    "tab.columns = tab.index\n",
    "tab"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Вопрос 8:**<br>\n",
    "Какой вид активности SVM определяет хуже всего в терминах точности? Полноты? <br>\n",
    "\n",
    "**Ответ:**\n",
    "- по точности – подъем вверх по лестнице, по полноте – лежание\n",
    "- по точности – лежание, по полноте – сидение\n",
    "- по точности – ходьба, по полноте – ходьба\n",
    "- по точности – сидение, по полноте – стояние"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Наконец, проделайте то же самое, что в 7 вопросе, только добавив PCA.\n",
    "\n",
    "- Используйте выборки `X_train_scaled` и `X_test_scaled`\n",
    "- Обучите тот же PCA, что раньше, на отмасшабированной обучающей выборке, примените преобразование к тестовой\n",
    "- Настройте гиперпараметр `C` на кросс-валидации по обучающей выборке с PCA-преобразованием. Вы заметите, насколько это проходит быстрее, чем раньше.\n",
    "\n",
    "**Вопрос 9:**<br>\n",
    "Какова разность между лучшим качеством (долей верных ответов) на кросс-валидации в случае всех 561 исходных признаков и во втором случае, когда применялся метод главных компонент? Округлите до целых процентов.<br>\n",
    "\n",
    "**Варианты:**\n",
    "- Качество одинаковое\n",
    "- 2%\n",
    "- 4% \n",
    "- 10%\n",
    "- 20%\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Вопрос 10:**<br>\n",
    "Выберите все верные утверждения:\n",
    "\n",
    "**Варианты:**\n",
    "- Метод главных компонент в данном случае позволил уменьшить время обучения модели, при этом качество (доля верных ответов на кросс-валидации) очень пострадало, более чем на 10%\n",
    "- PCA можно использовать для визуализации данных, однако для этой задачи есть и лучше подходящие методы, например, tSNE. Зато PCA имеет меньшую вычислительную сложность\n",
    "- PCA строит линейные комбинации исходных признаков, и в некоторых задачах они могут плохо интерпретироваться человеком"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
